package com.gregoiresage.pebble4gopro;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import com.getpebble.android.kit.PebbleKit;
import com.getpebble.android.kit.util.PebbleDictionary;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;

public class Pebble {
	
	public static final UUID GOPRO_UUID = UUID.fromString("A09C5E34-445C-4AC2-B694-F20C064D8332");
	
	public enum MessageKey {
		PEBBLE_CAMERA_CMD_NAME_KEY(0x0), 
		PEBBLE_BACPAC_CMD_NAME_KEY(0x01),
		PEBBLE_CMD_VALUE_KEY(0x02),
		GOPRO_STATUS_KEY(0x05),
		GOPRO_POWER_STATUS_KEY(0x06);
        private int value;
        private MessageKey(int value) {
                this.value = value;
        }
	}
	
	private boolean _bConnected = false;

	private BroadcastReceiver connectedReceiver;
	private BroadcastReceiver disconnectedReceiver;
	private PebbleKit.PebbleDataReceiver dataReceiver;
	private CommandReceiver commandReceiver;
	
	public boolean isConnected(){
		return _bConnected;
	}

	public void register(Context context){
		dataReceiver = new PebbleKit.PebbleDataReceiver(GOPRO_UUID) {
			@Override
			public void receiveData(final Context context, final int transactionId, final PebbleDictionary data) {
				String cameraCommandName = data.getString(MessageKey.PEBBLE_CAMERA_CMD_NAME_KEY.value);
				if(cameraCommandName != null){
					Long cameraCommandValue = data.getUnsignedInteger(MessageKey.PEBBLE_CMD_VALUE_KEY.value);
					if(cameraCommandValue != null){
						GoProCommand command = new GoProCommand(GoProCommand.Device.CAMERA, cameraCommandName, cameraCommandValue.intValue());
						Log.d("GOPRO", "Camera command received : " + command);
						if(commandReceiver != null)
							commandReceiver.receiveCommand(command);
						PebbleKit.sendAckToPebble(context, transactionId);
						return;
					}
				}

				String bacpacCommandName = data.getString(MessageKey.PEBBLE_BACPAC_CMD_NAME_KEY.value);
				if(bacpacCommandName != null){
					Long bacpacCommandValue = data.getUnsignedInteger(MessageKey.PEBBLE_CMD_VALUE_KEY.value);
					if(bacpacCommandValue != null){
						GoProCommand command = new GoProCommand(GoProCommand.Device.BACPAC, bacpacCommandName, bacpacCommandValue.intValue());
						Log.d("GOPRO", "Bacpac command received : " + command);
						if(commandReceiver != null)
							commandReceiver.receiveCommand(command);
						PebbleKit.sendAckToPebble(context, transactionId);
						return;
					}
				}

				PebbleKit.sendNackToPebble(context, transactionId);
			}
		};

		connectedReceiver =  new BroadcastReceiver(){
			@Override
			public void onReceive(Context context, Intent intent) {
				_bConnected = true;
			}
		};

		disconnectedReceiver =  new BroadcastReceiver(){
			@Override
			public void onReceive(Context context, Intent intent) {
				_bConnected = false;
			}
		};

		PebbleKit.registerReceivedDataHandler(context, dataReceiver);
		PebbleKit.registerPebbleConnectedReceiver(context, connectedReceiver);
		PebbleKit.registerPebbleDisconnectedReceiver(context, disconnectedReceiver);
		
		_bConnected = PebbleKit.isWatchConnected(context);
	}

	public void unregister(Context context){
		context.unregisterReceiver(dataReceiver);
		context.unregisterReceiver(connectedReceiver);
		context.unregisterReceiver(disconnectedReceiver);
	}
	
	public void registerCommandReceiver(CommandReceiver receiver){
		commandReceiver = receiver;
	}

	// Send a broadcast to launch the specified application on the connected Pebble
	public void startGoProWatchApp(Context context) {
		PebbleKit.startAppOnPebble(context, GOPRO_UUID);
	}

	// Send a broadcast to close the specified application on the connected Pebble
	public void stopGoProWatchApp(Context context) {
		PebbleKit.closeAppOnPebble(context, GOPRO_UUID);
	}

	// Push data to be displayed on Pebble's GoPro app.
	public void sendStatus(Context context, byte[] bData) {
		PebbleDictionary data = new PebbleDictionary();
		data.addBytes(MessageKey.GOPRO_STATUS_KEY.value, bData);
		PebbleKit.sendDataToPebble(context, GOPRO_UUID, data);
	}
	
	// Push the gopro power status on the pebble.
	public void sendPowerStatus(Context context, byte status) {
		PebbleDictionary data = new PebbleDictionary();
		data.addUint8(MessageKey.GOPRO_POWER_STATUS_KEY.value, status);
		PebbleKit.sendDataToPebble(context, GOPRO_UUID, data);
	}

	public interface CommandReceiver{
		public void receiveCommand(GoProCommand command);
	}
	
	private static final String WATCHAPP_FILENAME = "pebble-for-gopro.pbw";
	private static final String PEBBLE_LAUNCH_COMPONENT = "com.getpebble.android";
	private static final String PEBBLE_LAUNCH_ACTIVITY = "com.getpebble.android.ui.UpdateActivity";

	
	public void installWatchApp(Activity activity) {
		Log.d("GOPRO", "copy_watchapp_from_assets()");
	    InputStream input = null;
	    OutputStream output = null;
	    try {
	        input = activity.getApplicationContext().getAssets().open(WATCHAPP_FILENAME);
	        File dest = new File(activity.getApplicationContext().getExternalCacheDir().getAbsolutePath() + "/pebble-for-gopro.pbw");
	        // delete existing file
	        dest.delete();
	        output = new FileOutputStream(dest);
	        Log.d("GOPRO", "storing pbw: " + dest);
	        
	        // copy asset to file
	        byte[] buffer = new byte[2056];
	        int length;
	        while ((length = input.read(buffer))>0){
	            output.write(buffer, 0, length);
	        }
	        output.flush();
	        
	        // launch pebble update activity
	        Intent intent = new Intent(Intent.ACTION_VIEW);
	        intent.setData(Uri.fromFile(dest));
	        intent.setClassName(PEBBLE_LAUNCH_COMPONENT, PEBBLE_LAUNCH_ACTIVITY);
	        Log.d("GOPRO", "launch pebble to load pbw");
	        try {
	            activity.startActivity(intent);
	        } catch (ActivityNotFoundException e) {
	        	Log.e("GOPRO", "Pebble app not installed");
	        }
	    } catch (IOException e) {
	    	Log.e("GOPRO", e.getLocalizedMessage(), e);
	    } finally {
	        if (input != null) {
	            try {
	                input.close();
	            } catch (IOException e) {
	            	Log.e("GOPRO", e.getLocalizedMessage(), e);
	            }
	        }
	        if (output != null) {
	            try {
	                output.close();
	            } catch (IOException e) {
	            	Log.e("GOPRO", e.getLocalizedMessage(), e);
	            }
	        }
	    }
	}

}
